package game;
import java.awt.Point;
import java.util.*;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import xml.XMLizeable;

/**
 * <p>Creates a new instance of the game state once the player chooses their desired game.
 * It tracks the game state throughout the entirety of the game. It also swaps the pieces
 * and checks that moves are valid according to the pattern the current piece being moved.</p>
 * 
 * @author Nicole Waldrum
 * @date January 30, 2010
 * @version 1.0
 * 
 * @author Mark Kelly
 * @date February 24, 2010
 * @version 2.0
 * 
 * @author Trevor Bastian
 * @date February 23, 2010
 * @version 2.0
 * @version 4.0
 * 
 */
public class GameState implements MoveChecker, GameStateListener, XMLizeable {
	private ChessBoard board;
	public GameType gameType;
	private Set<MoveChecker> moveCheckers;
	private Set<GameStateListener> gameStateListeners;
	private Set<OutcomeListener> outcomeListeners;

	/**
	 * <p>Creates a new instance of {@code GameState}.</p>
	 * 
	 * @param gameType the type of game that the player has selected
	 */
	public GameState (GameType gameType) {
		moveCheckers = new HashSet<MoveChecker>();
		gameStateListeners = new HashSet<GameStateListener>();
		outcomeListeners = new HashSet<OutcomeListener>();

		this.gameType = gameType;
		gameType.setGameState(this);
		gameType.setupBoard();

		addMoveChecker(board);
		addMoveChecker(gameType);
	}

	/**
	 * <p>Creates a {@code GameState} with an initial {@code GameType} of {@code NoGame}.</p>
	 */
	public GameState() {
		this(new NoGame());
	}

	/**
	 * @return the current {@code GameType}
	 */
	public GameType getGameType() {
		return gameType;
	}

	/**
	 * @param board - the {@code ChessBoard} to use for the game
	 */
	public void setBoard(ChessBoard board) {
		this.board = board;
	}

	/**
	 * @return the <tt>ChessBoard</tt> that belongs to this <tt>GameState</tt>
	 */
	public ChessBoard getBoard() {
		return board;
	}

	/**
	 * <p>Add a <tt>MoveChecker</tt> to this <tt>GameState</tt></p>
	 * 
	 * @param mc the <tt>MoveChecker</tt> to add
	 */
	public void addMoveChecker(MoveChecker mc) {
		moveCheckers.add(mc);
	}

	/**
	 * <p>Removes a <tt>MoveChecker</tt> from this <tt>GameState</tt></p>
	 * 
	 * @param mc the <tt>MoveChecker</tt> to remove
	 */
	public void removeMoveChecker(MoveChecker mc) {
		moveCheckers.remove(mc);
	}

	/**
	 * <p>Verifies validity of a move with every <tt>MoveChecker</tt> that has associated itself with this <tt>GameState</tt></p>
	 * 
	 * @param piece the <tt>Piece</tt> that will move
	 * @param point the new location of the <tt>Piece</tt>
	 * 
	 * @return <tt>true</tt> if every <tt>MoveChecker</tt> returns <tt>true</tt>
	 */
	@Override
	public boolean isValidMove(Piece piece, Point point) {
		for (MoveChecker mc : moveCheckers)
			if (!mc.isValidMove(piece, point))
				return false;

		return (piece.isValidMove(piece, point));
	}

	/**
	 * <p>Add a <tt>GameStateListener</tt> to this <tt>GameState</tt></p>
	 * 
	 * @param gsl the <tt>GameStateListener</tt> to add
	 */
	public void addGameStateListener(GameStateListener gsl) {
		gameStateListeners.add(gsl);
		gsl.stateChanged(this);
	}

	/**
	 * <p>Remove a <tt>GameStateListener</tt> from this <tt>GameState</tt></p>
	 * 
	 * @param gsl the <tt>GameStateListener</tt> to remove
	 */
	public void removeGameStateListener(GameStateListener gsl) {
		gameStateListeners.remove(gsl);
	}

	/**
	 * <p>Notify all <tt>GameStateListener</tt> that a <tt>Piece</tt> has moved</p>
	 * 
	 * @param p the <tt>Piece</tt> that moved
	 */
	public void notifyPieceMoved(Piece p, ChessBoard b) {
		for (GameStateListener gsl : gameStateListeners)
			gsl.pieceMoved(this, p, b);
	}

	/**
	 * <p>Invoke the <tt>gameStarted()</tt> method of each <tt>GameStateListener</tt></p>
	 */
	public void notifyGameStarted() {
		for (GameStateListener gsl : gameStateListeners)
			gsl.gameStarted(this);
	}

	/**
	 * notifies all parties interested that the gamestate has changed
	 * @param gs - the gamestate to send to everybody
	 */
	private void notifyStateChanged(GameState gs) {
		for (GameStateListener gsl : gameStateListeners)
			gsl.stateChanged(gs);
	}

	/**
	 * Add an <tt>OutcomeListener</tt> to this <tt>GameState</tt>
	 * 
	 * @param ol the <tt>OutcomeListener</tt> to add
	 */
	public void addOutcomeListener(OutcomeListener ol) {
		outcomeListeners.add(ol);
	}

	/**
	 * Remove an <tt>OutcomeListener</tt> from this <tt>GameState</tt>
	 * 
	 * @param ol the <tt>OutcomeListener</tt> to remove
	 */
	public void removeOutcomeListener(OutcomeListener ol) {
		outcomeListeners.remove(ol);
	}

	/**
	 * Notify each <tt>OutcomeListener</tt> that the game has been won
	 * 
	 * @param team the <tt>Team</tt> that won
	 */
	public void notifyGameWon(Team team)
	{
		for (OutcomeListener ol : outcomeListeners)
			ol.gameWon(team);
	}

	/**
	 * Notify each <tt>OutcomeListener</tt> that the game has been tied
	 * }
	 * @param reason
	 */
	public void notifyGameTied(String reason) {
		for (OutcomeListener ol : outcomeListeners)
			ol.gameTied(reason);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void gameStarted(GameState gs) {
		notifyGameStarted();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void pieceMoved(GameState gs, Piece p, ChessBoard b) {
		notifyPieceMoved(p, b);		
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void stateChanged(GameState gs) {
		notifyStateChanged(gs);
	}

	@Override
	public void needPieceChoice(GameState gs) {
		// TODO Auto-generated method stub
		
	}

	public static GameState importFromXML(Element elem) {
		NodeList gt = elem.getElementsByTagName("GameType");
		NodeList board = elem.getElementsByTagName(ChessBoard.class.getName());

		GameType type = GameType.importFromXML((Element)gt.item(0));

		GameState gameState = new GameState(type);
		gameState.setBoard(ChessBoard.importFromXML((Element)board.item(0)));
		type.setGameState(gameState);

		return gameState;
	}

	@Override
	public Element toXML(Document doc) {
		Element gameState = doc.createElement(GameState.class.getName());
		gameState.appendChild(board.toXML(doc));
		gameState.appendChild(gameType.toXML(doc));
		return gameState;
	}
}